package handler

import (
	"filestore-rebuild/db"
	"filestore-rebuild/models"
	"filestore-rebuild/util"
	"github.com/gin-gonic/gin"
	"io"
	"io/ioutil"
	"log"
	"net/http"
	"os"
	"strconv"
)

// FileUploadHandler 文件上传接口
func FileUploadHandler(context *gin.Context) {
	var err error
	file, header, err := context.Request.FormFile("file")
	if err != nil {
		log.Printf("error recv from file: %s\n", err.Error())
		context.JSON(http.StatusOK, util.Resp{Code: -1, Msg: "error recv from file", Data: nil})
		return
	}
	fileName := header.Filename
	fileSha1 := util.Sha1File(file)
	var fileSize int64 = 0
	var fileTmpPath = ""
	// 存储到临时目录
	// file在上面 sha1File时文件指针移动到了末尾，需要重置到文件头
	file.Seek(0, 0)
	fileTmpPath, fileSize, err = storeLocalTmp(fileName, file)

	// uploadSuc := db.UploadFile(fileSha1, fileName, fileSize, fileTmpPath)
	err = db.InsertFile(models.File{FileSha1: fileSha1, FileName: fileName, FileSize: fileSize, FileAddr: fileTmpPath})
	if err != nil {
		context.JSON(http.StatusOK, util.Resp{Code: -1, Msg: "error upload file"})
		return
	}

	username := context.Request.Context().Value("username").(string)
	// userFileSuc := db.InsertUserFile(username, fileSha1, fileSize, fileName)
	err = db.InsertUserFileORM(&models.UserFile{UserName: username, FileSha1: fileSha1, FileSize: fileSize, FileName: fileName})
	if err != nil {
		context.JSON(http.StatusOK, util.Resp{Code: -1, Msg: err.Error()})
		return
	}

	context.JSON(http.StatusOK, util.Resp{Code: 1, Msg: "upload success"})

}

// FileListHandler 用户文件元信息接口
func FileListHandler(ctx *gin.Context) {
	username := ctx.Request.Context().Value("username").(string)
	files := db.ListUserFile(username)
	if files == nil || len(files) == 0 {
		ctx.JSON(http.StatusOK, util.Resp{Code: 1, Msg: "empty list", Data: nil})
		return
	}
	ctx.JSON(http.StatusOK, util.Resp{Code: 1, Msg: "Get file list success", Data: files})

}

// FileDeleteHandler 删除用户文件文件接口
func FileDeleteHandler(ctx *gin.Context) {
	username := ctx.Request.Context().Value("username").(string)
	filehash := ctx.Request.FormValue("filehash")
	filename := ctx.Request.FormValue("filename")
	deleteSuc := db.DeleteUserFileORM(models.UserFile{UserName: username, FileSha1: filehash, FileName: filename})
	if !deleteSuc {
		ctx.JSON(http.StatusOK, util.Resp{Code: -1, Msg: "Delete failed"})
		return
	}
	ctx.JSON(http.StatusOK, util.Resp{Code: 1, Msg: "Delete Success"})
}

// UserFileRenameHandler 文件重命名接口
func UserFileRenameHandler(ctx *gin.Context) {
	username := ctx.Request.Context().Value("username").(string)
	filehash := ctx.Request.FormValue("filehash")
	filename := ctx.Request.FormValue("filename")
	newname := ctx.Request.FormValue("newname")
	// renameSuc := db.UpdateUserFileName(username, filehash, filename, newname)
	renameSuc := db.UpdateUserFileNameORM(models.UserFile{UserName: username, FileSha1: filehash, FileName: filename}, newname)
	if !renameSuc {
		ctx.JSON(http.StatusOK, util.Resp{Code: -1, Msg: "Rename failed"})
		return
	}
	ctx.JSON(http.StatusOK, util.Resp{Code: 1, Msg: "Rename Success"})
}

// FastUploadHandler 快传接口
func FastUploadHandler(ctx *gin.Context) {
	filehash := ctx.Request.FormValue("filehash")
	filesize, err := strconv.ParseInt(ctx.Request.FormValue("filesize"), 10, 64)
	if err != nil {
		log.Printf("error atoi: %s\n", err.Error())
		ctx.JSON(http.StatusOK, util.Resp{Code: -1, Msg: "Error params"})
		return
	}

	// fileExists := db.CheckFileExists(filehash, filesize)
	// if !fileExists {
	// 	ctx.JSON(http.StatusOK, util.Resp{Code: 1, Msg: "Fast upload failed. Try simple upload"})
	// 	return
	// }
	_, err = db.SelectFileBySha1AndFileSize(filehash, filesize)
	if err != nil {
		ctx.JSON(http.StatusOK, util.Resp{Code: 1, Msg: "Fast upload failed. Try simple upload"})
		return
	}

	username := ctx.Request.Context().Value("username").(string)
	filename := ctx.Request.FormValue("filename")
	// insertSuc := db.InsertUserFile(username, filehash, filesize, filename)
	err = db.InsertUserFileORM(&models.UserFile{UserName: username, FileSha1: filehash, FileSize: filesize, FileName: filename})
	if err != nil {
		ctx.JSON(http.StatusOK, util.Resp{Code: -1, Msg: "Fast upload failed."})
		return
	}

	ctx.JSON(http.StatusOK, util.Resp{Code: 1, Msg: "Fast upload success"})

}

// DownloadFileHandler 下载文件接口
func DownloadFileHandler(ctx *gin.Context) {
	// 解析参数
	username := ctx.Request.Context().Value("username").(string)
	fileSha1 := ctx.Request.FormValue("filehash")
	fileName := ctx.Request.FormValue("filename")

	// 校验用户是否用有该文件且文件可用
	userFile, err := db.SelectUserFile(username, fileName, fileSha1)
	if err != nil {
		ctx.JSON(http.StatusOK, util.Resp{Code: -1, Msg: err.Error()})
		return
	}

	// 从唯一文件表中获取文件对象
	file, err := db.SelectFileBySha1AndFileSize(userFile.FileSha1, userFile.FileSize)
	if err != nil {
		ctx.JSON(http.StatusOK, util.Resp{Code: -1, Msg: err.Error()})
		return
	}

	// 打开文件读取内容
	downloadFile, err := os.Open(file.FileAddr)
	if err != nil {
		ctx.JSON(http.StatusOK, util.Resp{Code: -1, Msg: "open file error"})
		return
	}
	defer downloadFile.Close()

	readAll, err := ioutil.ReadAll(downloadFile)
	if err != nil {
		ctx.JSON(http.StatusOK, "read file error")
		return
	}
	ctx.Header("Content-Type", "application/octect-stream")
	ctx.Header("Content-Disposition", "attachment;filename="+"\""+userFile.FileName+"\"")
	ctx.Writer.Write(readAll)
}

// storeLocalTmp 将目标文件存储本地临时目录
func storeLocalTmp(filename string, file io.Reader) (filePath string, fileSize int64, err error) {
	fileStorePath := "/tmp/" + filename
	newFile, err := os.Create(fileStorePath)
	if err != nil {
		log.Printf("create tmp file failed : %s\n", err.Error())
		return "", 0, err
	}

	defer newFile.Close()

	fileSize, err = io.Copy(newFile, file)
	if err != nil {
		log.Printf("copy file failed: %s", err.Error())
		return "", 0, err
	}
	return fileStorePath, fileSize, nil
}
